package alfabankapi

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"time"
)

type Token struct {
	AccessToken  string `json:"access_token"`
	RefreshToken string `json:"refresh_token"`
	TokenType    string `json:"token_type"`
	ExpiresIn    int    `json:"expires_in"`
	IDToken      string `json:"id_token"`
	TokenExpires time.Time
}

// https://developers.alfabank.ru/products/alfa-api/documentation/articles/alfa-id/articles/acf-h2h/articles/get-access-token/v1/get-access-token?navFilter=h2h
func (c *ApiClient) ExchangeCodeForToken(code string) error {
	if code == "" {
		return errors.New("code is empty")
	}

	url := fmt.Sprintf("%s/oidc/token", c.URL)
	headers := map[string]string{
		"Accept":       "application/json",
		"Content-Type": "application/x-www-form-urlencoded",
	}
	body := fmt.Sprintf("grant_type=authorization_code&code=%s&client_id=%s&client_secret=%s&redirect_uri=%s",
		code, c.ClientID, c.clientSecret, c.RedirectURI)
	resp, err := c.makeRequest("POST", url, headers, []byte(body))
	if err != nil {
		return err
	}

	type TokenResponse struct {
		AccessToken  string `json:"access_token"`
		RefreshToken string `json:"refresh_token"`
		TokenType    string `json:"token_type"`
		ExpiresIn    int    `json:"expires_in"`
		IDToken      string `json:"id_token"`
	}
	var tokenResponse TokenResponse
	err = json.Unmarshal(resp, &tokenResponse)
	if err != nil {
		return err
	}

	c.token = &Token{} // Initialize c.token
	c.token.AccessToken = tokenResponse.AccessToken
	c.token.RefreshToken = tokenResponse.RefreshToken
	c.token.TokenType = tokenResponse.TokenType
	c.token.ExpiresIn = tokenResponse.ExpiresIn
	c.token.IDToken = tokenResponse.IDToken
	c.token.TokenExpires = time.Now().Add(time.Duration(c.token.ExpiresIn) * time.Second)

	err = c.storeToken()
	if err != nil {
		return err
	}
	return nil
}

// https://developers.alfabank.ru/products/alfa-api/documentation/articles/alfa-id/articles/acf-h2h/articles/token-update/v1/token-update?navFilter=h2h
func (c *ApiClient) RefreshToken() error {
	if c.token.RefreshToken == "" {
		return errors.New("refresh_token is empty")
	}
	url := fmt.Sprintf("%s/oidc/token", c.URL)
	headers := map[string]string{
		"Accept":       "application/json",
		"Content-Type": "application/x-www-form-urlencoded",
	}
	body := fmt.Sprintf("grant_type=refresh_token&refresh_token=%s&client_id=%s&client_secret=%s",
		c.token.RefreshToken, c.ClientID, c.clientSecret)
	resp, err := c.makeRequest("POST", url, headers, []byte(body))
	if err != nil {
		return err
	}

	type TokenResponse struct {
		AccessToken  string `json:"access_token"`
		RefreshToken string `json:"refresh_token"`
		TokenType    string `json:"token_type"`
		ExpiresIn    int    `json:"expires_in"`
		IDToken      string `json:"id_token"`
	}
	var tokenResponse TokenResponse
	err = json.Unmarshal(resp, &tokenResponse)
	if err != nil {
		return err
	}
	c.token = &Token{} // Initialize c.token
	c.token.AccessToken = tokenResponse.AccessToken
	c.token.RefreshToken = tokenResponse.RefreshToken
	c.token.TokenType = tokenResponse.TokenType
	c.token.ExpiresIn = tokenResponse.ExpiresIn
	c.token.IDToken = tokenResponse.IDToken
	c.token.TokenExpires = time.Now().Add(time.Duration(c.token.ExpiresIn) * time.Second)

	err = c.storeToken()
	if err != nil {
		return err
	}
	return nil
}

func (c *ApiClient) isTokenValid() bool {
	if c.token == nil || c.token.AccessToken == "" {
		return false
	}
	if c.token.TokenExpires.After(time.Now()) {
		return false
	}
	return true
}

func (c *ApiClient) storeToken() error {
	file, err := os.Create(c.tokenFile)
	if err != nil {
		return err
	}
	defer file.Close()

	jsonEncoder := json.NewEncoder(file)
	err = jsonEncoder.Encode(c.token)
	if err != nil {
		return err
	}

	return nil
}
func (c *ApiClient) ReadTokenFromFile() error {
	data, err := os.ReadFile(c.tokenFile)
	if err != nil {
		return err
	}
	var token Token
	err = json.Unmarshal(data, &token)
	if err != nil {
		return err
	}
	c.token = &token
	ifValid := c.isTokenValid()
	if !ifValid == false {
		return fmt.Errorf("token is not valid")
	}
	return nil
}
